﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Reactive.Linq;
using System.Threading;
using System.Windows.Input;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Threading;
using TfL;

namespace Tfl.Controls.WPF.ViewModel
{
    public class DesignTimeStationArrivalsViewModel : StationArrivalsViewModel
    {
        public DesignTimeStationArrivalsViewModel()
        {
            Station = "Victoria - design time";
            Names = new List<string>(new string[] { "Vict", "Bank", "London Bridge" });
            Times = new ObservableCollection<PlatformViewModel>(new PlatformViewModel[]
                                                                    {
                                                                       new PlatformViewModel()
                                                                       {   Line="Victoria",
                                                                           Platform = "Westbound",
                                                                           Arrivals = new ObservableCollection<TrainArrivalViewModel>(new TrainArrivalViewModel[]
                                                                                                                                          {
                                                                                                                                           new TrainArrivalViewModel(){Destination = "Brixton",TimeToArrival = new TimeSpan(0,0,4,30)}  
                                                                                                                                          })
                                                                       },  
                                                                    });
            //Times = new TrainTimesForStation("Victoria")
            //{
            //    Name = "Victoria",
            //    Platforms = new List<Platform>(new Platform[]
            //                                                   {
            //                                                       new Platform{Name="Westbound",
            //                                                           Trains = new List<Train>(new Train[]
            //                                                                                        {
            //                                                                                            new Train
            //                                                                                                {
            //                                                                                                    Destination = "Egglestone",
            //                                                                                                    Location = "Nearby",
            //                                                                                                    TimeToStation = new TimeSpan(0,0,1,1)
            //                                                                                                }
            //                                                                                        })}, 
            //                                                   })
            //};
        }
    }

    /// <summary>
    /// This class contains properties that a View can data bind to.
    /// <para>
    /// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
    /// </para>
    /// <para>
    /// You can also use Blend to data bind with the tool's support.
    /// </para>
    /// <para>
    /// See http://www.galasoft.ch/mvvm/getstarted
    /// </para>
    /// </summary>
    public class StationArrivalsViewModel : ViewModelBase
    {
        /// <summary>
        /// Initializes a new instance of the StationArrivalsViewModel class.
        /// </summary>
        public StationArrivalsViewModel()
        {
            ////if (IsInDesignMode)
            ////{
            ////    // Code runs in Blend --> create design time data.
            ////}
            ////else
            ////{
            ////    // Code runs "for real": Connect to service, etc...
            ////}
            StartCommand = new RelayCommand(Start, () => !string.IsNullOrEmpty(Station));
            _times = new ObservableCollection<PlatformViewModel>();
            _times.CollectionChanged += TimesCollectionChanged;
            _names = new List<string>();
            _tokenSource = new CancellationTokenSource();
        }

        public ICommand StartCommand { get; set; }



        public StationArrivalsViewModel(IStationTrainTimesFeed feed, IStationNameFeed namesFeed)
        {
            StartCommand = new RelayCommand(Start, () => !string.IsNullOrEmpty(Station));
            _times = new ObservableCollection<PlatformViewModel>();
            _times.CollectionChanged += TimesCollectionChanged;
            _names = new List<string>();
            _tokenSource = new CancellationTokenSource();
            _feed = feed;
            (from n in namesFeed.Poll()
             select n).Subscribe(OnNextStationName);
            
        }

        void TimesCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            RaisePropertyChanged("Times");
        }

        public string Station
        {
            get { return _station; }
            set
            {
                _station = value;
                RaisePropertyChanged("Station");
                
            }
        }

        public List<string> Names
        {
            get { return _names; }
            set 
            { 
                _names = value; 
                RaisePropertyChanged("Names");
            }
        }

        public ObservableCollection<PlatformViewModel> Times
        {
            get { return _times; }
            set
            {
                _times = value;
                RaisePropertyChanged("Times");
            }
        }

        private void Stop()
        {
            if(_tokenSource != null)
            {
                _tokenSource.Cancel();
            }
            if(_subscriber != null)
            {
                _subscriber.Dispose();
            }
            _times.Clear();

        }

        private void Start()
        {
            Stop();

            _tokenSource = new CancellationTokenSource();

            var arrivals = from a in _feed.Station(Station).Poll()
                           select a;
            _subscriber = arrivals.Subscribe(OnNextArrival);


        }

        private void OnNextArrival(TrainTimesForStation arrivals)
        {
            if(arrivals == null)
            {
                DispatcherHelper.CheckBeginInvokeOnUI(() => _times.Clear());
            }
            else
            {
                foreach (var platform in arrivals.Platforms)
                {
                    var vm = new PlatformViewModel
                                 {
                                     Line = arrivals.Line,
                                     Platform = platform.Name,
                                     Arrivals = new ObservableCollection<TrainArrivalViewModel>(
                                         platform.Trains.ConvertAll(
                                             (arrival) =>
                                             new TrainArrivalViewModel
                                                 {
                                                     Destination = arrival.Destination,
                                                     TimeToArrival = arrival.TimeToStation
                                                 }))
                                 };
                    DispatcherHelper.CheckBeginInvokeOnUI(() => _times.Add(vm));
                }
            }
        }

        private void OnNextStationName(StationName name)
        {
            Names.Add(name.Name);
            Names.Sort();
        }

        public override void Cleanup()
        {
            // Clean own resources if needed
            base.Cleanup();
            _subscriber.Dispose();
            _tokenSource.Cancel();
        }
        private readonly IStationTrainTimesFeed _feed;
        private string _station = string.Empty;
        private CancellationTokenSource _tokenSource;
        private List<string> _names;
        private IDisposable _subscriber;
        private ObservableCollection<PlatformViewModel> _times;

    }
}